

/*
 * Copyright (c) 2021 Huawei Device Co.,   Ltd.
 * Licensed under the Apache License,   Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,   software
 * distributed under the License is distributed on an "AS IS" BASIS,  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,   either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef <NAMESPACE>_<SUBSYS_NAME>_FRAMEWORKS_JS_NAPI_<KEY_WORD>_UTILS_H_
#define <NAMESPACE>_<SUBSYS_NAME>_FRAMEWORKS_JS_NAPI_<KEY_WORD>_UTILS_H_
#include <string.h>
#include <nocopyable.h>
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include "napi/native_common.h"
#include "napi/native_api.h"
#include "hilog_wrapper.h"

namespace <namespace> {
namespace <Subsys_name> {

#define DEBUG_NAPI_CALL_PROMISE(env,  theCall) \
    do {                                     \
        napi_status status_; \
        if ((status_ = (theCall)) != napi_ok) {          \
            <KEY_WORD>_HILOGE(<KEY_WORD>_JS_NAPI,  "failed to call "#theCall", status = %{public}d", \
                (int)status_);\
            return (AsyncPromise(env, __FUNCTION__, nullptr));                \
        }                                    \
    } while (0)

#define DEBUG_NAPI_CALL(env,  theCall) \
    do {                                     \
        napi_status status_;\
        if ((status_ = (theCall)) != napi_ok) {          \
            <KEY_WORD>_HILOGE(<KEY_WORD>_JS_NAPI,  "failed to call "#theCall", status = %{public}d", \
                (int)status_); \
            return nullptr;                  \
        }                                    \
    } while (0)

#define DEBUG_NAPI_ASSERT(env, assertion, message) \
    do {                                                                                     \
        if (!(assertion)) {                                                                  \
            <KEY_WORD>_HILOGE(<KEY_WORD>_JS_NAPI,  message); \
            return nullptr;                                                                   \
        }                                                                                    \
    } while (0)

#define DEBUG_NAPI_CALL_NO_RET(env,  theCall) \
    do {                                     \
        napi_status status_;\
        if ((status_ = (theCall)) != napi_ok) {          \
            <KEY_WORD>_HILOGE(<KEY_WORD>_JS_NAPI,  "failed to call "#theCall", status = %{public}d", \
                (int)status_); \
            return; \
        }                                    \
    } while (0)

#define DEBUG_NAPI_ASSERT_NO_RET(env, assertion, message) \
    do {                                                                                     \
        if (!(assertion)) {                                                                  \
            <KEY_WORD>_HILOGE(<KEY_WORD>_JS_NAPI,  message); \
            return;\
        }                                                                                    \
    } while (0)

static inline napi_value priv_get_value(napi_env env, int32_t val) {
    napi_value value;
    napi_create_int32(env, val, &value);
    return value;
}
#define DECLARE_CONST_VAR(var) DECLARE_NAPI_STATIC_PROPERTY(#var, priv_get_value(env, (int32_t)var))

#define NAPI_OBJECT_PROP_GET(obj, type, prop, result) do {\
    napi_value val;  \
    napi_valuetype vtype;\
    DEBUG_NAPI_CALL(env,  napi_typeof(env,  obj,  &vtype));\
    DEBUG_NAPI_ASSERT(env,  vtype == napi_object, "Wrong argument typr. Obeject expected");\
    DEBUG_NAPI_CALL(env, napi_get_named_property(env, obj, prop, &val));\
    DEBUG_NAPI_CALL(env, napi_get_value_##type(env, val, result));\
}while(0)

#define NAPI_OBJECT_PROP_GET_WITH_LEN(obj, type, prop, len, result) do {\
    napi_value val;  \
    napi_valuetype otype;\
    napi_valuetype vtype;\
    DEBUG_NAPI_CALL(env,  napi_typeof(env,  obj,  &otype));\
    DEBUG_NAPI_ASSERT(env,  otype == napi_object, "Wrong argument typr. Obeject expected");\
    DEBUG_NAPI_CALL(env, napi_get_named_property(env, obj, prop, &val));\
    DEBUG_NAPI_CALL(env, napi_typeof(env,  val,  &vtype));\
    <KEY_WORD>_HILOGD(<KEY_WORD>_JS_NAPI,  "attri:%{public}s type is %{public}d", prop, vtype); \
    if (vtype == napi_string) {\
        DEBUG_NAPI_CALL(env, napi_get_value_##type(env, val, result, sizeof(result), &len));\
    } else {\
        result[0] = '\0';\
    }\
}while(0)

#define NAPI_OBJECT_PROP_GET_BUFFER(obj, prop, len, result) do {\
    napi_value val, buffer;  \
    napi_valuetype vtype;\
    napi_typedarray_type type;\
    size_t offset = 0;\
    DEBUG_NAPI_CALL(env,  napi_typeof(env,  obj,  &vtype));\
    DEBUG_NAPI_ASSERT(env,  vtype == napi_object, "Wrong argument typr. Obeject expected");\
    DEBUG_NAPI_CALL(env, napi_get_named_property(env, obj, prop, &val));\
    DEBUG_NAPI_CALL(env, napi_get_typedarray_info(env,  val,  &type,  reinterpret_cast<size_t*>(&len), \
        (void **)&result,  &buffer,  &offset));\
}while(0)

// 结构体属性构造
#define NAPI_OBJECT_PROP_SET(obj, type, prop, result) do {\
    napi_value val;  \
    DEBUG_NAPI_CALL(env, napi_create_##type(env, result, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

// 创建子结构体
#define NAPI_OBJECT_OBJ_SET(obj, prop, val) do {\
    DEBUG_NAPI_CALL(env, napi_create_object(env, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

#define NAPI_OBJECT_PROP_SET_WITH_LEN(obj, type, prop, len, result) do {\
    napi_value val;  \
    DEBUG_NAPI_CALL(env, napi_create_##type(env, result, len, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

#define NAPI_OBJECT_PROP_SET_BOOL(obj, prop, result) do {\
    napi_value val;\
    NAPI_CALL(env, napi_get_boolean(env, result,  &val));\
    DEBUG_NAPI_CALL(env,  napi_set_named_property(env, obj, prop, val));\
}while(0)

#define NAPI_OBJECT_PROP_SET_BUFFER(obj, prop, len, result) do {\
    napi_value val;  \
    napi_value arrayBuffer;\
    void *buffer;\
    DEBUG_NAPI_CALL(env, napi_create_arraybuffer(env, len, &buffer, &arrayBuffer));\
    memcpy(buffer, result, len);\
    DEBUG_NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, len, arrayBuffer, 0, &val));\
    DEBUG_NAPI_CALL(env, napi_set_named_property(env, obj, prop, val));\
}while(0)

#define MAX_ARGS  8
typedef napi_status (*async_excute_work)(napi_env env, size_t argc, napi_value args[], void *data);
typedef napi_value (*async_result_work)(napi_env env, void *data);
typedef void (*async_free_work)(napi_env env, void *data);

class AsyncCallback {
 public:
    AsyncCallback(napi_env env,
        size_t argc, napi_value args[],
        async_excute_work work,
        async_result_work result_work,
        async_free_work free_work,
        void   *data = nullptr,
        napi_value this_arg = nullptr,
        void *priv_data = nullptr);

    ~AsyncCallback();

    DISALLOW_COPY(AsyncCallback);

    napi_status ExcuteWork(napi_env env);
    napi_value ResultWork(napi_env env);
    void FreeWork(napi_env env);

 public:
    napi_deferred  deferred;
    napi_ref   ref_callback;
    size_t     argc_n;
    napi_value args_v[MAX_ARGS];

    napi_status status;
    async_excute_work   excute_work;
    async_result_work   result_work;
    async_free_work     free_work;
    napi_async_work     async_work;
    void   *data_ptr;
};

// 一般调用
napi_value NapiCall(const napi_env& env, AsyncCallback *async);
// 异步回调
napi_value AsyncCall(const napi_env& env, const napi_value& callback,
    const std::string& res_name, AsyncCallback *async);

// 异步Promise
napi_value AsyncPromise(const napi_env& env, const std::string& res_name, AsyncCallback *async);
// 同步Promise
napi_value SyncPromise(const napi_env &env, napi_status status, napi_value result);

/**
 * @brief 数组转换成napi数组
 * 
 */
typedef napi_value (*napi_create_index_func)(napi_env env, const void *data);

template<class T>
napi_value CppToJsArray(const napi_env& env,  const std::vector<T>& arrays, napi_create_index_func create_func)
{
    napi_value result = nullptr;
    DEBUG_NAPI_CALL(env, napi_create_array_with_length(env,  arrays.size(),  &result));

    for (int i = 0; i < static_cast<int>(arrays.size()); i++) {
        const T &t = arrays[i];
        napi_value res = (create_func)(env, (const void*)(&t));
        DEBUG_NAPI_CALL(env, napi_set_element(env,  result,  i,  res));
    }
    return result;
}

template<class T>
napi_value CppToJsArray(const napi_env& env,  T* arrays, int32_t num_arrays, napi_create_index_func create_func)
{
    napi_value result = nullptr;
    DEBUG_NAPI_CALL(env, napi_create_array_with_length(env,  num_arrays,  &result));

    for (int i = 0; i < num_arrays; i++) {
        napi_value res = (create_func)(env, &arrays[i]);
        DEBUG_NAPI_CALL(env, napi_set_element(env,  result,  i,  res));
    }
    return result;
}

#if 0
// 例子
napi_value example(napi_env env)
{
    // 数组转napi
    int mms[5] = {1, 5, 3, 7, 8};
    // 通过lambda 表达式作为回调函数进行单个元素的数据类型转换
    napi_value result = CppToJsArray(env, mms, 5, [](napi_env env, void *data, napi_value *val) {
        int *d = reinterpret_cast<int *>(data);
        DEBUG_NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, *d, val));
    });
    return result;
}
#endif
}  // namespace <Subsys_name>
}  // namespace <namespace>
#endif  // <NAMESPACE>_<SUBSYS_NAME>_FRAMEWORKS_JS_NAPI_<KEY_WORD>_UTILS_H_
